<?php

hc_core_ClassLoader::load('halo_AbstractHandlerMapping');

class halo_CakelikeHandlerMapping extends halo_AbstractHandlerMapping {

    protected $routes = array();

    public function __construct(array $setup) {
        foreach ($setup as $path => $pathSetup) {
            $this->routes[] = $this->connect($path, $pathSetup);
        }
    }

    public function connect($path, $setup) {

        if ( ! is_array($setup) ) {
            $setup = array('controller' => $setup);
        }

        if ( ! array_key_exists('params', $setup) ) {
            $setup['params'] = array();
        }

        foreach ( $setup['params'] as $paramName => $config ) {
            if ( ! is_array($config) ) {
                $config = $setup['params'][$paramName] = array('default' => $config);
            }
            if ( ! array_key_exists('match', $config) ) {
                $setup['params'][$paramName]['match'] = '[^\/]+';
            }
        }
        $params = $setup['params'];
        $parsed = array();
        $names = array();
        if ( empty($path) || ($path == '/') ) {
            $pattern = '/^[\/]*$/';
        } else {
            $parts = explode('/', $path);
            foreach ( $parts as $part ) {

                if ( empty($part) ) continue;

                $q = null;
                $part = trim($part);

                // See if this part has any named params.
                $hasNamedParam = strpos($part, ':') !== false;

                //print " [ $part " . ( $hasNamedParam ? 'has' : 'does not have' ) . " named param ]\n";

                if ( $hasNamedParam and preg_match('/^:([^:]+)$/', $part, $matches) ) {
                    // We have a named param, and the entire section is a named param...
                    $paramName = $matches[1];
                    if ( array_key_exists($paramName, $params) ) {
                        if ( array_key_exists('optional', $params[$paramName]) and $params[$paramName]['optional'] ) {
                            // If we have a default
                            $q = '?';
                        }
                        if ( array_key_exists('match', $params[$paramName]) ) {
                            $parsed[] = '(?:/(' . $params[$paramName]['match'] . ')' . $q . ')' . $q;
                        } else {
                            $parsed[] = '(?:/([^\/]+)' . $q . ')' . $q;
                        }
                    } else {
                        $parsed[] = '(?:/([^\/]+))';
                    }
                    $names[] = $paramName;
                } else {
                    $parsed[] = '/' . $part;
                }


            }
        }

        return array(
            'pattern' => '#^' . join('', $parsed) . '[\/]*$#',
            'names' => $names,
            'controller' => $setup['controller'],
            'params' => $params,
        );

    }

    protected function getHandlerInternal(halo_HttpRequest $httpRequest) {

        $url = $httpRequest->getRequestedUrl();
        if ( strpos($url, '/') !== 0 ) $url = '/' . $url;

        foreach ( $this->routes as $route  ) {
            //echo '<pre>';
            //echo 'Checking url ' . $url . ' against ' . $route['pattern'] . "\n";
            //echo '</pre>';
            if ( $routeConfig = $this->checkMatch($url, $route) ) {
                foreach ( $routeConfig as $paramName => $paramValue ) {
                    $httpRequest->setUrlParam($paramName, $paramValue);
                }
                return $this->context->get($routeConfig['controller']);
            }
        }

        return null;

    }
    public function checkMatch($url, $route) {
        if ( preg_match($route['pattern'], $url, $matches) ) {
            $params = array();
            array_shift($matches);
            for ( $i = 0; $i < count($matches); $i++ ) {
                $params[$route['names'][$i]] = $matches[$i];
            }
            //echo '<pre>';
            //echo " / matched! \n";
            foreach ( $route['params'] as $paramName => $config ) {
                if ( array_key_exists('default', $config) and ( ( ! array_key_exists($paramName, $params) ) or $params[$paramName] == null ) ) {
                    $params[$paramName] = $config['default'];
                }
            }
            if ( ! array_key_exists('controller', $params) ) {
                $params['controller'] = $route['controller'];
            }
            //print_r($matches);
            //print_r($route);
            //print_r($params);
            //echo '</pre>';
            return $params;
        }
        return null;
    }

}

?>
